package com.gitee.l0km.casban.ctor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.gitee.l0km.casban.CasbanScanners;
import com.gitee.l0km.casban.CommonFunctions;
import com.gitee.l0km.casban.CommonPredicates;
import com.gitee.l0km.casban.ctor.annotations.ConstructType;
import com.gitee.l0km.casban.ctor.annotations.CtorArg;
import com.gitee.l0km.casban.ctor.annotations.Setter;
import com.gitee.l0km.casban.exception.PackageScanException;
import com.gitee.l0km.com4j.base.NameStringUtils;
import com.gitee.l0km.com4j.basex.reflection.MethodUtils;
import com.gitee.l0km.common.spring.core.annotation.AnnotationUtils;
import com.gitee.l0km.common.spring.core.annotation.MergedAnnotation;
import com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.gitee.l0km.aocache.Singleton.weakSingletonOf;
import static com.gitee.l0km.aocache.Singleton.newInstance;
/**
 * @author guyadong
 * @since 1.1.0
 */
public class AnnotConstructors {
	
	private AnnotConstructors() {
	}

	private static class CtorFieldHelper implements Predicate<Method>,Comparator<Method>{
		private final Map<String, Object> attrs;
		public CtorFieldHelper(final MergedAnnotation<? extends Annotation> mergedAnnotation, boolean ignoreDefault) {
			attrs = mergedAnnotation.filterAttributes(!ignoreDefault ? Predicates.<String>alwaysTrue(): new Predicate<String>() {

				@Override
				public boolean apply(String input) {
					return mergedAnnotation.hasNonDefaultValue(input);
				}
			}).asMap();
		}
		@Override
		public int compare(Method o1, Method o2) {
			ConstructType c1 = AnnotationUtils.getAnnotation(o1, ConstructType.class);
			ConstructType c2 = AnnotationUtils.getAnnotation(o1, ConstructType.class);
			return Integer.compare(c1.value(), c2.value());
		}
		@Override
		public boolean apply(Method input) {
			return attrs.containsKey(input.getName());
		}
	}
	/** 
	 * 查找 {@link ConstructType} 标记的字段定义的要构建的类 
	 * @param mergedAnnotation
	 * @param ignoreDefault 为{@code true}忽略为默认值的字段
	 */
	public static Class<?> getConstructType(MergedAnnotation<? extends Annotation> mergedAnnotation, boolean ignoreDefault) {
		checkArgument(mergedAnnotation != null && mergedAnnotation.isPresent(),"mergedAnnotation is null or no present");
			Set<Method> methods = CasbanScanners.findMembersOfClass(
					mergedAnnotation.getType(),
					CommonFunctions.asMethodOrnull(),
					CommonPredicates.hasAnnotationMemberFilter(ConstructType.class));
			CtorFieldHelper helper = new CtorFieldHelper(mergedAnnotation,ignoreDefault);
			ImmutableList<Method> ctorMembers = FluentIterable.from(methods).filter(helper).toSortedList(helper);
			for(Method method:ctorMembers) {
				Object value = mergedAnnotation.getValue(method.getName()).get();
				Class<?> clazz = null;
				if(value instanceof String) {
					if(!((String)value).isEmpty()) {
						try {
							clazz = Class.forName((String)value);
						} catch (ClassNotFoundException e) {
							throw new IllegalArgumentException(e);
						}
					}
				}else if (value instanceof Class) {
					clazz = (Class<?>)value;
				}else {
					throw new IllegalArgumentException(String.format("Class<?> Or String  required for @%s.%s() from %s",
						mergedAnnotation.getType().getName(),
						method.getName(),
						mergedAnnotation.getSource()));
				}
				if(null != clazz && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
					Class<?> expect= method.getAnnotation(ConstructType.class).expect();
					checkArgument(expect.isAssignableFrom(clazz),"%s not subtype of %s  for @%s.%s() from %s",
							clazz.getName(),
							expect.getName(),
							mergedAnnotation.getType().getName(),
							method.getName(),
							mergedAnnotation.getSource());
					return clazz;
				}
			}
			throw new IllegalArgumentException(String.format(
					"NOT FOUND MATCHED valid field to fetch Class with @%s in [%s] of %s from %s",
					ConstructType.class.getSimpleName(),
					Joiner.on(',').join(FluentIterable.from(methods).transform(CommonFunctions.getNameFun())),
					mergedAnnotation.synthesize(),mergedAnnotation.getSource()));
	}
	/** 
	 * 查找 {@link ConstructType} 标记的字段定义的要构建的类 
	 * @param annot
	 * @see #getConstructType(MergedAnnotation, boolean)
	 */
	public static Class<?> getConstructType(Annotation annot) {
		return getConstructType(TypeMappedAnnotations.from(checkNotNull(annot,"annot is null")), true);
	}

	/**
	 * 从注解中查找 {@link CtorArg}标记的构造方法参数<br>
	 * @param mergedAnnotation
	 * @return 以构造方法参数定义顺序返回参数名(注解字段名)-参数值的映射,输入参数为{@code null}返回空Map
	 * @since 1.2.9
	 */
	public static Map<String, Object> getConstructorArgsAsMap(
			MergedAnnotation<? extends Annotation> mergedAnnotation) {
		LinkedHashMap<String, Object> args = new LinkedHashMap<>();
		if (null != mergedAnnotation) {
			int index = 0;
			try {
				for (;; ++index) {
					Method method = CasbanScanners.findAccessibleObject(mergedAnnotation.getType(), false,
							CommonFunctions.asMethodOrnull(),
							CommonPredicates.containedEntryMemberFilter(CtorArg.class, "value", index));
					if (null == method) {
						break;
					}
					args.put(method.getName(), mergedAnnotation.getValue(method.getName()).get());
				}
			} catch (PackageScanException e) {
				throw new PackageScanException(String.format("FOUND MULTI field with @%s(%s) in %s \ncaused by %s",
						CtorArg.class.getSimpleName(), index, mergedAnnotation, e.getMessage()), e);
			}
		}
		return Collections.unmodifiableMap(args);
	}
	/**
	 * 从注解中查找 {@link CtorArg}标记的构造方法参数
	 * @param mergedAnnotation
	 * @return 构造方法参数数组,输入参数为{@code null}返回空数组
	 * @see #getConstructorArgsAsMap(MergedAnnotation)
	 */
	public static Object[] getConstructorArgs(MergedAnnotation<? extends Annotation> mergedAnnotation){
		return getConstructorArgsAsMap(mergedAnnotation).values().toArray(new Object[0]);
	}
	/**
	 * @see #getConstructorArgs(MergedAnnotation)
	 */
	public static Object[] getConstructorArgs(Annotation annot){
		return getConstructorArgs(null == annot ? null : TypeMappedAnnotations.from(annot));
	}

	/**
	 * 从注解中查找 {@link Setter}标记的赋值参数定义
	 * @param mergedAnnotation
	 * @return 赋值参数数组,输入参数为{@code null}返回空数组
	 */
	public static List<MergedAnnotation<Setter>> settersOf(MergedAnnotation<? extends Annotation> mergedAnnotation){
		if(null != mergedAnnotation) {
			Set<Method> args =
			CasbanScanners.findMembersOfClass(mergedAnnotation.getType(), 
					CommonFunctions.asMethodOrnull(),
					CommonPredicates.hasAnnotationMemberFilter(Setter.class));
			return FluentIterable.from(args).transform(CommonFunctions.getMeragedAnnotationFun(Setter.class))
					  .filter(new Predicate<MergedAnnotation<Setter>>() {
						  @Override public boolean apply(MergedAnnotation<Setter> input) { 
							  return input.hasNonDefaultValue("value"); 
						  }})
					 .toList();
		}
		return Collections.emptyList();
	}
	/**
	 * 对象赋值，将{@code sourceAnnotation}中由{@code setter}标记的字段值以反射方式赋值到{@code instance}的指定字段
	 * @param instance
	 * @param sourceAnnotation
	 * @param setter
	 * @throws ReflectiveOperationException
	 */
	public static void assign(Object instance,Annotation sourceAnnotation, MergedAnnotation<Setter>setter) throws ReflectiveOperationException{
		if(null != instance && null != setter) {
			Object source = setter.getSource();
			Setter annot = setter.synthesize();
			checkArgument(source instanceof Method,"Method required for source of %s",annot);
			Method method = (Method)source;
			checkArgument(Annotation.class.isAssignableFrom(method.getDeclaringClass()),"Annotation method required for source of %s",annot);
			String  name;
			if(annot.name().isEmpty()) {
				name = "set"+NameStringUtils.firstUpperCase(method.getName());
			}else {
				name = annot.name();
			}
			Object value = method.invoke(sourceAnnotation);
			MethodUtils.invokeMethod(instance, name, new Object[] {value});
			
		}
	}
	/**
	 * 对象赋值，将{@code sourceAnnotation}中由{@code setters}标记的字段值以反射方式赋值到{@code instance}的指定字段
	 * @param instance
	 * @param sourceAnnotation
	 * @param setters
	 * @throws ReflectiveOperationException
	 */
	public static void assign(Object instance,Annotation sourceAnnotation, Iterable<MergedAnnotation<Setter>>setters) throws ReflectiveOperationException{
		if(null != instance && null != sourceAnnotation && null !=setters) {
				for(MergedAnnotation<Setter> setter:setters) {
					assign(instance,sourceAnnotation, setter);
				}
		}
	}

	/**
	 * 根据注解中{@link ConstructType}标记字段定义的的类型创建实例<br>
	 * 如果注解为元注解，则尝试从元注解所在的注解中查找{@link CtorArg}标记的字段作为构造方法参数。
	 * 
	 * @param mergedAnnotation
	 * @param ctorArgStrategy  参见 {@link CtorArgStrategy}
	 * @param modfier          用于对从{@code mergedAnnotation}生成的{@link CtorArgInfo}进行修改,为{@code null}忽略
	 * @return 返回构建的对象
	 * @throws ReflectiveOperationException
	 * @since 1.2.9
	 */
	@SuppressWarnings("unchecked")
	public static <T> T construct(MergedAnnotation<? extends Annotation> mergedAnnotation,
			Function<MergedAnnotation<?>, CtorArgInfo> ctorArgStrategy, Function<CtorArgInfo, CtorArgInfo> modfier)
			throws ReflectiveOperationException {
		Class<?> clazz = getConstructType(mergedAnnotation, true);
		CtorArgInfo argInfo = checkNotNull(ctorArgStrategy, "ctorArgStrategy is null").apply(mergedAnnotation);
		if (null != modfier) {
			argInfo = modfier.apply(argInfo);
		}
		/** 注解中获取构造方法参数列表 */
		Object[] args = argInfo.args();
		List<MergedAnnotation<Setter>> setters = settersOf(argInfo.source);
		if (setters.isEmpty()) {
			return (T) weakSingletonOf(clazz, args);
		}
		Object instance = newInstance(clazz, args);
		assign(instance, argInfo.source.synthesize(), setters);
		return (T) instance;
	}
	/**
	 * 根据注解中{@link ConstructType}标记字段定义的的类型创建实例<br>
	 * 如果注解为元注解，则尝试从元注解所在的注解中查找{@link CtorArg}标记的字段作为构造方法参数。
	 * @param mergedAnnotation
	 * @param ctorArgStrategy 参见 {@link CtorArgStrategy}
	 * @return 返回构建的对象
	 * @throws ReflectiveOperationException
	 * @see #construct(MergedAnnotation, Function, Function)
	 */
	public static <T> T construct(MergedAnnotation<? extends Annotation> mergedAnnotation, 
			Function<MergedAnnotation<?>, CtorArgInfo> ctorArgStrategy)throws ReflectiveOperationException {
		return construct(mergedAnnotation,ctorArgStrategy,null);
	}
	/**
	 * 根据注解中{@link ConstructType}标记字段定义的的类型创建实例<br>
	 * 构造方法参数从当前注解查找，参见 {@link CtorArgStrategy#SELF_ONLY}
	 * @param mergedAnnotation
	 * @return 返回构建的对象
	 * @throws ReflectiveOperationException
	 */
	public static <T> T construct(MergedAnnotation<? extends Annotation> mergedAnnotation)throws ReflectiveOperationException {
		return construct(mergedAnnotation,CtorArgStrategy.SELF_ONLY);
	}
	/**
	 * 从{@link MergedAnnotation}获取{@link CtorArg}标记的构造方法参数的策略,<br>
	 * 返回对象列表永不为空，，第一个元素为参数所在注解，之后的其他元素是构造方法参数
	 * @see AnnotConstructors#getConstructorArgs(MergedAnnotation)
	 * @author guyadong
	 */
	public static enum CtorArgStrategy implements Function<MergedAnnotation<?>, CtorArgInfo>{
		/**
		 * 只从当前注解查找
		 */
		SELF_ONLY,
		/**
		 * 自上而下返回查找所有注解，直到找到或达到root
		 */
		UNTIL_ROOT {
			@Override
			public CtorArgInfo apply(MergedAnnotation<?> input) {
				CtorArgInfo argInfo;
				while((argInfo =getCtorArgInfoOf(input)).size()==0 && null != input && input.isMetaPresent()) {
					input = input.getMetaSource();
				}
				return argInfo;
			}
		},
		/**
		 * 从第一个非元注解的源注解对象查找
		 */
		NOMETA {
			@Override
			public CtorArgInfo apply(MergedAnnotation<?> input) {
				while(null != input && input.isMetaPresent()) {
					input = input.getMetaSource();
				}
				return getCtorArgInfoOf(input);
			}
		},
		/**
		 * 先查找当前注解如果找不到则从第一个非元注解的源注解对象查找
		 */
		SELF_OR_NOMETA{
			@Override
			public CtorArgInfo apply(MergedAnnotation<?> input) {
				CtorArgInfo argInfo;
				if((argInfo =SELF_ONLY.apply(input)).size()==0) {
					argInfo = NOMETA.apply(input);
				}
				return argInfo;
			}
		}, 
		/***
		 * 从根注解查找
		 */
		ROOT {
			@Override
			public CtorArgInfo apply(MergedAnnotation<?> input) {
				return getCtorArgInfoOf(null != input ? input.getRoot() : input);
			}
		};
		
		@Override
		public CtorArgInfo apply(MergedAnnotation<?> input) {
			return getCtorArgInfoOf(input);
		}

		private static CtorArgInfo getCtorArgInfoOf(MergedAnnotation<? extends Annotation> mergedAnnotation){
			return new CtorArgInfo(mergedAnnotation,getConstructorArgsAsMap(mergedAnnotation));
		}
	}
	/**
	 * 封装从注解中获取的构造方法参数名参数值及注解对象
	 * @author guyadong
	 * @since 1.2.9
	 */
	public static class CtorArgInfo{
		/**
		 * 数据来源注解对象
		 */
		public final MergedAnnotation<? extends Annotation> source;
		/**
		 * 以构造方法参数定义顺序返回参数名(注解字段名)-参数值的映射
		 */
		public final Map<String, Object> argsMap;
		public CtorArgInfo(MergedAnnotation<? extends Annotation> source, Map<String, Object> argsMap) {
			this.source = source;
			this.argsMap = new LinkedHashMap<>(argsMap);
		}
		/**
		 * 以数组形式返回构造方法参数
		 */
		public Object[] args() {
			return  argsMap.values().toArray(new Object[argsMap.size()]);
		}
		/**
		 * 以数组形式返回注解中定义构造方法参数的字段名
		 */
		public String[] argNames() {
			return argsMap.keySet().toArray(new String[argsMap.size()]);
		}
		/**
		 * 返回构造参数数量
		 */
		public int size() {
			return argsMap.size();
		}
		@Override
		public int hashCode() {
			return Objects.hash(argsMap, source);
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			CtorArgInfo other = (CtorArgInfo) obj;
			return Objects.equals(argsMap, other.argsMap) && Objects.equals(source, other.source);
		}
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("CtorArgInfo [source=").append(source).append(", argsMap=").append(argsMap).append("]");
			return builder.toString();
		}
	}
}
